/*************************************************************/
/*                                                           */
/*     MC823 - Laboratorio de Teleprocessamento e Redes      */
/*     1 Semestre / 2013                                     */
/*     Projeto 1 - Cliente e Servidor TCP de uma Livraria    */
/*                                                           */
/*     Alunos: Joao Gabriel M.D.Mendes - RA 102788           */
/*             Lucas Domingues Lima    - RA 103140           */
/*                                                           */
/*************************************************************/

/* Arquivo: /servidor/tcp/servidor.c
   Definicao: Implementa funcoes principais do servidor em si,
   			  gerando o executavel final.
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <time.h>
#include <signal.h>
#include "../livraria.h"
#include "../manipulamsgs.h"
#include "../../sendrecv.h"

#define PORT "3491"  // the port users will be connecting to

#define BACKLOG 10	 // how many pending connections queue will hold

/* NANO_PER_SECOND define o numero de nanosegundos em um segundo */
#define NANO_PER_SECOND	1000000000

void sigchld_handler(int s)
{
	while(waitpid(-1, NULL, WNOHANG) > 0);
}

// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
	if (sa->sa_family == AF_INET) {
		return &(((struct sockaddr_in*)sa)->sin_addr);
	}

	return &(((struct sockaddr_in6*)sa)->sin6_addr);
}



int main(void)
{
	int sockfd, new_fd, numbytes, fimConexao;  // listen on sock_fd, new connection on new_fd
	struct addrinfo hints, *servinfo, *p;
	struct sockaddr_storage their_addr; // connector's address information
	socklen_t sin_size;
	struct sigaction sa;
	int yes=1;
	char s[INET6_ADDRSTRLEN];
	char *buf,*aux,arquivoLog[50],arquivoTempo[50];
	int rv;
	t_estoque *estoque;
	char *id;
    FILE *arqLog,*arqTempo;
    struct timespec tstart;
    struct timespec tstop;
    float driftnano;


	//Inicializa socket TCP
	
	memset(&hints, 0, sizeof hints);
	hints.ai_family = AF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_flags = AI_PASSIVE; // use my IP

	if ((rv = getaddrinfo(NULL, PORT, &hints, &servinfo)) != 0) {
		fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
		return 1;
	}

	// loop through all the results and bind to the first we can
	for(p = servinfo; p != NULL; p = p->ai_next) {
		if ((sockfd = socket(p->ai_family, p->ai_socktype,
				p->ai_protocol)) == -1) {
			perror("server: socket");
			continue;
		}

		if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,
				sizeof(int)) == -1) {
			perror("setsockopt");
			exit(1);
		}

		if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
			close(sockfd);
			perror("server: bind");
			continue;
		}

		break;
	}

	if (p == NULL)  {
		fprintf(stderr, "server: failed to bind\n");
		return 2;
	}

	freeaddrinfo(servinfo); // all done with this structure

	if (listen(sockfd, BACKLOG) == -1) {
		perror("listen");
		exit(1);
	}

	sa.sa_handler = sigchld_handler; // reap all dead processes
	sigemptyset(&sa.sa_mask);
	sa.sa_flags = SA_RESTART;
	if (sigaction(SIGCHLD, &sa, NULL) == -1) {
		perror("sigaction");
		exit(1);
	}

	//Socket criado com sucesso. Aguardando conexoes.
	
	printf("server: waiting for connections...\n");

	estoque = criaEstoque();

	while(1) 
	{  // main accept() loop
		sin_size = sizeof their_addr;
		new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
		//Volta ao inicio caso não exista nova conexao.
		if (new_fd == -1) {
			perror("accept");
			continue;
		}
		//se houver nova conexao, da seguimento a partir daqui.
		
		inet_ntop(their_addr.ss_family,
	    get_in_addr((struct sockaddr *)&their_addr),s, sizeof s);

		if (recvall(new_fd, &buf, &numbytes) == -1) 
	    {
	    	 perror("recv");
	    	 exit(1);
	    }

        printf("server: got connection from %s::%s\n", s,buf);

		//Confirma estabelecimento de conexao junto ao cliente
        numbytes = strlen("Bem-vindo*");
		if (sendall(new_fd, "Bem-vindo*", &numbytes) == -1)
	        perror("sendall");
		
		if (!fork()) { // if this is the child process
			close(sockfd); // child doesn't need the listener
            sprintf(arquivoLog,"log/%s::%s",s,buf);
            sprintf(arquivoTempo,"tempo/%s::%s",s,buf);

            arqLog = fopen(arquivoLog,"w");
            arqTempo = fopen(arquivoTempo,"w");
			fimConexao = false;
                        
			while(!fimConexao)
			{
			    if (recvall(new_fd, &buf, &numbytes) == -1) 
			    {
	    			perror("recv");
	    			exit(1);
			    }
                            
                fprintf(arqLog,"Cliente: %s\n",buf);
                /* Primeiro, obtenho o tempo inicial. */
		        clock_gettime(CLOCK_REALTIME, &tstart);
		        // Trata requisicao do cliente e retorna string com resposta
			    aux = trataRequisicaoCliente(estoque,buf);
                /* Para obter o tempo final */
		        clock_gettime(CLOCK_REALTIME, &tstop);
                /* Calcula-se o tempo gasto */
		        driftnano = (float)(tstop.tv_sec - tstart.tv_sec);
				driftnano += (tstop.tv_nsec - tstart.tv_nsec)/(float)NANO_PER_SECOND;
				
				//se houver resposta valida, a envia para cliente.
			    if(aux != NULL)
			    {				
			        fprintf(arqLog,"Servidor: %s\n",aux);	
				    numbytes = strlen(aux);
			        if (sendall(new_fd, aux, &numbytes) == -1)
			            perror("sendall");
		            /* Grava no arquivo */
		            fprintf(arqTempo,"%d %.9f\n",buf[0]-'A'+1,driftnano);
                    free(aux);
                    free(buf);
                }
			    else
			    {
			    	//Mensagem de resposta e nula, o que indica fim de
			    	//conexao.
	                fimConexao = true;
			    }
			}

            printf("server: ending connection %s\n",s);
            fclose(arqLog);
			close(new_fd);
			exit(0);
		}
		close(new_fd);  // parent doesn't need this
	}
	return 0;
}

